using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using Konst3d.Efficiency.Suite;
using System.Collections.Specialized;

namespace Konst3d.Efficiency.Suite.SparsedBinary
{
    internal class BuyCollectionStorage : IBuyCollection, IBuyStorage, IDisposable
    {
        #region Constants

        private const string BUY_FILE_EXT = ".buys";

        #endregion // Constants

        #region Create/Open/Delete Methods

        public static BuyCollectionStorage Create(string fileName, bool overwrite, ICryptographicEngine cryptographicEngine) // file name w/o ext
        {
            Contract.Requires(!String.IsNullOrEmpty(fileName));

            BuyFile file = null;
            LabelsStorage labels = null;

            try
            {
                file = BuyFile.Create(fileName + BUY_FILE_EXT, overwrite);
                labels = LabelsStorage.Create(fileName, overwrite, cryptographicEngine);

                return new BuyCollectionStorage(file, labels);
            }
            catch
            {
                if (file != null)
                {
                    file.Dispose();
                }

                if (labels != null)
                {
                    labels.Dispose();
                }

                throw;
            }
        }

        public static BuyCollectionStorage Open(string fileName, ICryptographicEngine cryptographicEngine)
        {
            Contract.Requires(!String.IsNullOrEmpty(fileName));

            BuyFile file = null;
            LabelsStorage labels = null;

            try
            {
                file = BuyFile.Open(fileName + BUY_FILE_EXT);
                labels = LabelsStorage.Open(fileName, cryptographicEngine);

                return new BuyCollectionStorage(file, labels);
            }
            catch
            {
                if (file != null)
                {
                    file.Dispose();
                }

                if (labels != null)
                {
                    labels.Dispose();
                }

                throw;
            }
        }

        public void Delete()
        {
            Contract.Assert(!_disposed);

            _file.Delete();
            _labels.Delete();

            Dispose();
        }

        #endregion // Create/Open/Delete Methods

        #region Constructor/Finalizer

        private BuyCollectionStorage(BuyFile file, LabelsStorage labels)
        {
            Contract.Requires(file != null);
            Contract.Requires(labels != null);

            _file = file;
            _labels = labels;

            var entries = _file.GetAll();

            foreach (var entry in entries)
            {
                _buys.Add(entry.Key,
                    new Buy(this, entry.Key, entry.Value.State, _labels.GetLabel(entry.Value.TitleID), _labels.GetLabel(entry.Value.DescriptionID), entry.Value.Severity, entry.Value.Price, entry.Value.DeadLine, entry.Value.Created));
            }
        }

        #endregion // Constructor/Finalizer

        #region IBuyCollection Members

		public int Count
		{
			get
			{
				return _buys.Count;
			}
		}

        public IBuy this[int id]
        {
            get
            {
                Contract.Assert(_buys.ContainsKey(id));

                return _buys[id];
            }
        }

        public int Add(string title, string description, int severity, double price, DateTime created, DateTime deadLine)
        {
            Contract.Requires(deadLine > created);

            int titleID = _labels.AddLabel(title);
            int descrID = _labels.AddLabel(description);
            int id      = _file.AddEntry(titleID, descrID, BuyState.Active, severity, price, created, deadLine);

            _buys.Add(id, new Buy(this, id, BuyState.Active, title, description, severity, price, deadLine, created));

            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }

            return id;
        }

        public void Remove(int id)
        {
            Contract.Requires(_buys.ContainsKey(id));

            _labels.DeleteLabel(_file.GetEntryTitleID(id));
            _labels.DeleteLabel(_file.GetEntryDescrID(id));

            _file.RemoveEntry(id);
            _buys.Remove(id);

            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }

        #endregion

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        #endregion

        #region IEnumerable<IBuy> Members

        public IEnumerator<IBuy> GetEnumerator()
        {
            return _buys.Values.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _buys.Values.GetEnumerator();
        }

        #endregion

        #region IBuyStorage Members

        void IBuyStorage.UpdateBuyState(int id, BuyState state)
        {
            Contract.Requires(id >= 0);
            Contract.Requires(_buys.ContainsKey(id));

            _file.SetEntryState(id, state);

            if (CollectionChanged != null) // TOFIX: optimize it...
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }

        void IBuyStorage.UpdateBuyTitle(int id, string title)
        {
            Contract.Requires(id >= 0);
            Contract.Requires(_buys.ContainsKey(id));

            int titleID = _file.GetEntryTitleID(id);
            int newTitleID = _labels.UpdateLabel(titleID, title);

            if (titleID != newTitleID)
            {
                _file.SetEntryTitleID(id, newTitleID);
            }

            if (CollectionChanged != null) // TOFIX: optimize it...
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }

        void IBuyStorage.UpdateBuyDescription(int id, string description)
        {
            Contract.Requires(id >= 0);
            Contract.Requires(_buys.ContainsKey(id));

            int descrID = _file.GetEntryDescrID(id);
            int newDescrID = _labels.UpdateLabel(descrID, description);

            if (descrID != newDescrID)
            {
                _file.SetEntryDescriptionID(id, newDescrID);
            }

            if (CollectionChanged != null) // TOFIX: optimize it...
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }

        void IBuyStorage.UpdateBuySeverity(int id, int severity)
        {
            Contract.Requires(id >= 0);
            Contract.Requires(_buys.ContainsKey(id));

            _file.SetEntrySeverity(id, severity);

            if (CollectionChanged != null) // TOFIX: optimize it...
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }

        void IBuyStorage.UpdateBuyPrice(int id, double price)
        {
            Contract.Requires(id >= 0);
            Contract.Requires(_buys.ContainsKey(id));

            _file.SetEntryPrice(id, price);

            if (CollectionChanged != null) // TOFIX: optimize it...
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }

        void IBuyStorage.UpdateBuyDeadLine(int id, DateTime deadLine)
        {
            Contract.Requires(id >= 0);
            Contract.Requires(_buys.ContainsKey(id));

            _file.SetEntryDeadLine(id, deadLine);

            if (CollectionChanged != null) // TOFIX: optimize it...
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }

        void IBuyStorage.UpdateBuyCreated(int id, DateTime created)
        {
            Contract.Requires(id >= 0);
            Contract.Requires(_buys.ContainsKey(id));

            _file.SetEntryCreated(id, created);

            if (CollectionChanged != null) // TOFIX: optimize it...
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }

        #endregion // IBuyStorage Members

        #region IDisposable Members

        public void Dispose()
        {
            GC.SuppressFinalize(this);
            Dispose(true);
        }

        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _file.Dispose();
                    _file = null;

                    _labels.Dispose();
                    _labels = null;

                    _buys.Clear();
                    _buys = null;
                }

                _disposed = true;
            }
        }

        #endregion

        #region Fields

        private bool _disposed = false;
        private BuyFile _file;
        private LabelsStorage _labels;
        private Dictionary<int, Buy> _buys = new Dictionary<int, Buy>();

        #endregion // Fields
    }
}
